/*
 * The LevelDB-CLI Project
 * Copyright (C) 2024 JuRuoqwq<BitPasta@163.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "run_cmd.h"

#include "parse_input.h"
#include "command_line.h"
#include <leveldb/status.h>
#include <leveldb/write_batch.h>
#include <leveldb/iterator.h>

#include <iostream>
#include <string>

RunCmd::~RunCmd()
{
    delete db_;
    db_ = nullptr; // 避免悬垂指针
}

void RunCmd::Run(const std::string command)
{
    std::vector<Token> tokens = parse_.GetToken(command);

    if (tokens.size() == 0) {
        return;
    }

    Token token = tokens[0];
    // 第0个是命令，剩下是数据等

    if (token.type <= CLEAR) {
        // Database command
        RunDBCommand(tokens);
    } else if (token.type >= OPTIONS && token.type <= WRITE_OPTIONS) {
        RunConfig(tokens);
    } else if (token.type >= HELP) {
        // 原生命令(如quit)
        RunNativeCommand(tokens);
    } else {
        std::cerr << "Invalid command \"" << token.source << "\"!" << std::endl;
        std::string promptStr = prompt_.GetCorrectInput(token.source, COMMAND);
        if (promptStr == "") {
            std::cerr << "Type \"help\" for more information!" << std::endl;
        } else {
            std::cerr << "Did you mean \"" << promptStr << "\"?" << std::endl;
        }
        return;
    }
}

void RunCmd::RunDBCommand(const std::vector<Token> tokens)
{
    leveldb::Status status;

    if (db_ == nullptr) {
        std::cerr << "No database are opened!" << std::endl;
        return;
    }
    
    // C++ Error 546
    std::string value;
    size_t argCount = tokens.size();
    switch (tokens[0].type) {
        case SET:
            if (argCount == 3) {
                status = db_->Put(writeOptions_, tokens[1].source, tokens[2].source);
                if (!status.ok()) {
                    std::cerr << "Unable to complete operation! [" << status.ToString() << "]" << std::endl;
                    return;
                } else {
                    std::cout << "OK!" << std::endl;
                }
            } else {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }
            break;
        case GET:
            if (argCount == 2) {
                status = db_->Get(readOptions_, tokens[1].source, &value);
                if (value.empty()) {
                    std::cout << "(nil)" << std::endl;
                } else {
                    std::cout << value << std::endl;
                }
            } else {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }
            break;
        case DELETE:
            if (argCount == 2) {
                status = db_->Delete(writeOptions_, tokens[1].source);
                if (!status.ok()) {
                    std::cerr << "Unable to complete operation! [" << status.ToString() << "]" << std::endl;
                    return;
                } else {
                    std::cout << "OK!" << std::endl;
                }
            } else {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }
            break;
        case SHOW:
            if (argCount < 2 || argCount > 3) {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }

            if (tokens[1].source == "prefix") {
                if (argCount != 3) {
                    std::cerr << "Incorrect amount of arguments in command \"show prefix\"!" << std::endl;
                    return;
                }
                status = ShowKeyValues(tokens[2].source);
            } else if (tokens[1].source == "all") {
                if (argCount != 2) {
                    std::cerr << "Incorrect amount of arguments in command \"show all\"!" << std::endl;
                    return;
                }
                status = ShowKeyValues("");
            } else {
                std::cerr << "Invalid sub-command \"" << tokens[1].source << "\"!" << std::endl;
                std::string promptStr = prompt_.GetCorrectInput(tokens[1].source, SUBCMD);
                if (promptStr == "") {
                    std::cerr << "Type \"help\" for more information!" << std::endl;
                } else {
                    std::cerr << "Did you mean \"" << promptStr << "\"?" << std::endl;
                }
                return;
            }
            break;
        case CLOSE:
            delete db_;
            db_ = nullptr;
            // 初始化配置
            options_ = leveldb::Options();
            writeOptions_ = leveldb::WriteOptions();
            readOptions_ = leveldb::ReadOptions();
            std::cout << "OK!" << std::endl;
            break;
        case CLEAR:
            if (tokens.size() == 1) {
                std::cerr << "Caution: This is a DANGEROUS OPERATION," << std::endl;
                std::cerr << "use \"clear --force(-f)\" to continue." << std::endl;
                return;
            } else if (tokens.size() == 2) {
                if (tokens[1].source == "--force" || tokens[1].source == "-f") {
                    status = Clear();
                    if (!status.ok()) {
                        std::cerr << "Unable to complete operation! [" << status.ToString() << "]" << std::endl;
                        return;
                    } else {
                        std::cout << "OK!" << std::endl;
                    }
                } else {
                    std::cerr << "Invalid argument \"" << tokens[1].source <<
                        "\" in command \"clear\"." << std::endl;
                    // clear只有--force一个argument
                    std::cerr << "Did you mean \"--force\" or \"-f\"?" << std::endl;
                }
            } else {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }
            break;
        default:
            break;
    }
}

void RunCmd::RunConfig(const std::vector<Token> tokens)
{
    // 由于格式为[option] [variable] [value],所以必须有3个token
    if (tokens.size() != 3) {
        std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
        return;
    }
    
    if (!IsValidVariable(tokens[1])) {
        std::cerr << "Invalid LevelDB vairable \"" << tokens[1].source << "\"!" << std::endl;
        std::string promptStr = prompt_.GetCorrectInput(tokens[1].source, VARIABLE);
        if (promptStr == "") {
            std::cerr << "Please refer the LevelDB documentation." << std::endl;
        } else {
            std::cerr << "Did you mean \"" << promptStr << "\"?" << std::endl;
        }
        return;
    }

    int configValue = parse_.ToValue(tokens[2]);
    if (configValue == INT32_MIN) {
        // 出现错误
        std::cerr << "Invalid value \"" << tokens[2].source << "\" for variable \""
            << tokens[1].source << "\"!" << std::endl;
        std::string promptStr = prompt_.GetCorrectInput(tokens[2].source, VALUE);
        if (promptStr == "") {
            std::cerr << "LevelDB just allow boolean and number." << std::endl;
        } else {
            std::cerr << "Did you mean \"" << promptStr << "\"?" << std::endl;
        }
        return;
    }

    if (tokens[0].type == OPTIONS) {
        switch (tokens[1].type) {
            // 要设置的变量
            case CREATE_IF_MISSING:
                options_.create_if_missing = (bool)configValue;
                break;
            case ERROR_IF_EXISTS:
                options_.error_if_exists = (bool)configValue;
                break;
            case PARANOID_CHECKS:
                options_.paranoid_checks = (bool)configValue;
                break;
            case WRITE_BUFFER_SIZE:
                options_.write_buffer_size = configValue;
                break;
            case MAX_OPEN_FILES:
                options_.max_open_files = configValue;
                break;
            case BLOCK_SIZE:
                options_.block_size = configValue;
                break;
            case BLOCK_RESTART_INTERVAL:
                options_.block_restart_interval = configValue;
                break;
            case MAX_FILE_SIZE:
                options_.max_file_size = configValue;
                break;
            case ZSTD_COMPRESSION_LEVEL:
                options_.zstd_compression_level = configValue;
                break;
            case REUSE_LOGS:
                options_.reuse_logs = (bool)configValue;
                break;
            default:
                return; // 这样就不会输出后面的"OK"
        }
    } else if (tokens[0].type == READ_OPTIONS) {
        switch (tokens[1].type) {
            case VERIFY_CHECKSUMS:
                readOptions_.verify_checksums = (bool)configValue;
                break;
            case FILL_CACHE:
                readOptions_.fill_cache = (bool)configValue;
                break;
            default:
                return;
        }
    } else if (tokens[0].type == WRITE_OPTIONS) {
        switch (tokens[1].type) {
            case SYNC:
                writeOptions_.sync = (bool)configValue;
                break;
            default:
                return;
        }
    } else {
        return;
    }
    std::cout << "OK!" << std::endl;
}

void RunCmd::RunNativeCommand(const std::vector<Token> tokens)
{
    // 原生命令没有arguments(open除外)
    if (tokens.size() != 1 && tokens[0].type != OPEN) {
        std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
        return;
    }

    leveldb::Status status;
    CommandLine commandLine;
    switch (tokens[0].type) {
        case HELP:
            commandLine.PrintHelp();
            break;
        case VERSION:
            commandLine.PrintVersion();
            break;
        case QUIT:
            delete db_;
            db_ = nullptr;
            std::exit(0);
        case OPEN:
            if (db_ != nullptr) {
                std::cerr << "There is a database opening!" << std::endl;
                return;
            }
            if (tokens.size() == 2) {
                status = leveldb::DB::Open(options_, tokens[1].source, &db_);
                if (!status.ok()) {
                    std::cerr << "Unable to complete operation! [" << status.ToString() << "]" << std::endl;
                    return;
                } else {
                    std::cout << "OK!" << std::endl;
                }
            } else {
                std::cerr << "Incorrect amount of arguments in command \"" << tokens[0].source << "\"!" << std::endl;
                return;
            }
            break;
        default:
            break;
    }
}

leveldb::Status RunCmd::Clear()
{
    leveldb::Status status;
    leveldb::WriteBatch batch;
    leveldb::Iterator *it = db_->NewIterator(readOptions_);
    for (it->SeekToFirst(); it->Valid(); it->Next()) {
        // 使用WriteBatch对磁盘IO的损耗较小
        batch.Delete(it->key());
    }
    delete it;
    it = nullptr;
    status = db_->Write(writeOptions_, &batch);
    return status;
}

int RunCmd::IsValidVariable(const Token token) const
{
    switch (token.type) {
        case CREATE_IF_MISSING:
        case ERROR_IF_EXISTS:
        case PARANOID_CHECKS:
        case WRITE_BUFFER_SIZE:
        case MAX_OPEN_FILES:
        case BLOCK_SIZE:
        case BLOCK_RESTART_INTERVAL:
        case MAX_FILE_SIZE:
        case ZSTD_COMPRESSION_LEVEL:
        case REUSE_LOGS:
        case VERIFY_CHECKSUMS:
        case FILL_CACHE:
        case SYNC:
            return true;
        default:
            return false;
    }
}

leveldb::Status RunCmd::ShowKeyValues(const std::string prefix) const
{
    leveldb::Iterator* it = db_->NewIterator(leveldb::ReadOptions());
    it->SeekToFirst();
    // 数据库为空
    if (!it->Valid()) {
        std::cout << "(nil)" << std::endl;
        delete it;
        it = nullptr;
        return leveldb::Status::OK();
    }
    
    // 如果prefix为空，说明是all模式，输出所有
    if (prefix == "") {
        for (; it->Valid(); it->Next()) {
            std::cout << "{" << it->key().ToString() << " : "  << it->value().ToString() << "}" << std::endl;
        }
    } else {
        // 已经做过判断，只有prefix模式
        bool isFound = false; // 用于判断是否需要输出nil
        for (; it->Valid(); it->Next()) {
            // 判断该key是否以prefix为前缀
            if (it->key().ToString().compare(0, prefix.length(), prefix) == 0) {
                std::cout << "{" << it->key().ToString() << " : "  << it->value().ToString() << "}" << std::endl;
                isFound = true;
            }
        }
        if (isFound == false) {
            std::cout << "(nil)" << std::endl;
        }
    }
      
    if (!it->status().ok()) {
        return it->status();
    }
    delete it;
    it = nullptr;
    return leveldb::Status::OK();
}